/*
 * Copyright 2019-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package me.zhengjie.utils;

import cn.hutool.core.date.LocalDateTimeUtil;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * @author: liaojinlong
 * @date: 2020/6/11 16:28
 * @apiNote: JDK 8  新日期类 格式化与字符串转换 工具类
 */
public class DateUtil {

    public static final DateTimeFormatter DFY_MD_HMS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    public static final DateTimeFormatter DFY_MD = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    public static final DateTimeFormatter DFY_M = DateTimeFormatter.ofPattern("yyyy-MM");

    /**
     * 日期格式，年月日，例如：20050630，20080808
     */
    public static final String FORMAT_YYYYMMDD = "yyyyMMdd";

    /**
     * 日期格式，年月日，用横杠分开，例如：2006-12-25，2008-08-08
     */
    public static final String FORMAT_YYYY_MM_DD = "yyyy-MM-dd";


    /**
     * 日期格式，年月日，例如：2016年10月05日
     */
    public static final String FORMAT_CHINA_YYYY_MM_DD = "yyyy年MM月dd日";

    /**
     * 日期格式，年月日，例如：2016年10月05日 12:00
     */
    public static final String FORMAT_CHINA_YYYY_MM_DD_HH_MM = "yyyy年MM月dd日 HH:mm";

    /**
     * 日期格式，年月日，例如：2016100512:00:99
     */
//	public static final String FORMAT_YYYYMMDDHHMMSS = "yyyyMMddHH:mm:ss";

    /**
     * 日期格式，年月日时分，例如：2000-12-30 12:00，2008-08-08 20:08
     */
    public static final String FORMAT_YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";

    /**
     * 日期格式，年月日时分，例如：2000-12-30 12:00，2008-08-08 20:08
     */
    public static final String FORMAT_YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";

    /**
     * LocalDateTime 转时间戳
     *
     * @param localDateTime /
     * @return /
     */
    public static Long getTimeStamp(LocalDateTime localDateTime) {
        return localDateTime.atZone(ZoneId.systemDefault()).toEpochSecond();
    }

    /**
     * 时间戳转LocalDateTime
     *
     * @param timeStamp /
     * @return /
     */
    public static LocalDateTime fromTimeStamp(Long timeStamp) {
        return LocalDateTime.ofEpochSecond(timeStamp, 0, OffsetDateTime.now().getOffset());
    }

    /**
     * LocalDateTime 转 Date
     * Jdk8 后 不推荐使用 {@link Date} Date
     *
     * @param localDateTime /
     * @return /
     */
    public static Date toDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * LocalDate 转 Date
     * Jdk8 后 不推荐使用 {@link Date} Date
     *
     * @param localDate /
     * @return /
     */
    public static Date toDate(LocalDate localDate) {
        return toDate(localDate.atTime(LocalTime.now(ZoneId.systemDefault())));
    }


    /**
     * Date转 LocalDateTime
     * Jdk8 后 不推荐使用 {@link Date} Date
     *
     * @param date /
     * @return /
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * 日期 格式化
     *
     * @param localDateTime /
     * @param patten /
     * @return /
     */
    public static String localDateTimeFormat(LocalDateTime localDateTime, String patten) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern(patten);
        return df.format(localDateTime);
    }

    /**
     * 日期 格式化
     *
     * @param localDateTime /
     * @param df /
     * @return /
     */
    public static String localDateTimeFormat(LocalDateTime localDateTime, DateTimeFormatter df) {
        return df.format(localDateTime);
    }

    /**
     * 日期格式化 yyyy-MM-dd HH:mm:ss
     *
     * @param localDateTime /
     * @return /
     */
    public static String localDateTimeFormatyMdHms(LocalDateTime localDateTime) {
        return DFY_MD_HMS.format(localDateTime);
    }

    /**
     * 日期格式化 yyyy-MM-dd
     *
     * @param localDateTime /
     * @return /
     */
    public String localDateTimeFormatyMd(LocalDateTime localDateTime) {
        return DFY_MD.format(localDateTime);
    }

    /**
     * 日期格式化 yyyy-MM
     *
     * @param localDateTime /
     * @return /
     */
    public String localDateTimeFormatyM(LocalDateTime localDateTime) {
        return DFY_M.format(localDateTime);
    }


    /**
     * 字符串转 LocalDateTime ，字符串格式 yyyy-MM-dd
     *
     * @param localDateTime /
     * @return /
     */
    public static LocalDateTime parseLocalDateTimeFormat(String localDateTime, String pattern) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDateTime.from(dateTimeFormatter.parse(localDateTime));
    }

    /**
     * 字符串转 LocalDateTime ，字符串格式 yyyy-MM-dd
     *
     * @param localDateTime /
     * @return /
     */
    public static LocalDateTime parseLocalDateTimeFormat(String localDateTime, DateTimeFormatter dateTimeFormatter) {
        return LocalDateTime.from(dateTimeFormatter.parse(localDateTime));
    }

    /**
     * 字符串转 LocalDateTime ，字符串格式 yyyy-MM-dd HH:mm:ss
     *
     * @param localDateTime /
     * @return /
     */
    public static LocalDateTime parseLocalDateTimeFormatyMdHms(String localDateTime) {
        return LocalDateTime.from(DFY_MD_HMS.parse(localDateTime));
    }

    /**
     * 获取Calendar
     * */
    public static Calendar getCalendar(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal;
    }

    /**
     * 获取某日期的年份
     */
    public static Integer getYear(Date date) {
        return getCalendar(date).get(Calendar.YEAR);
    }

    /**
     * 获取某日期的月份
     */
    public static Integer getMonth(Date date) {
        return getCalendar(date).get(Calendar.MONTH) + 1;
    }

    /**
     * 获取某日期的日
     */
    public static Integer getDayOfMonth(Date date) {
        return getCalendar(date).get(Calendar.DAY_OF_MONTH);
    }

    /**
     * 获取某日期的分钟
     */
    public static Integer getMinute(Date date) {
        return getCalendar(date).get(Calendar.MINUTE);
    }

    /**
     * 获取某日期的秒
     */
    public static Integer getSecond(Date date) {
        return getCalendar(date).get(Calendar.SECOND);
    }

    /**
     * 获取某个日期为星期几 (星期日~星期六)
     */
    public static String getDayOfWeek(Date date) {
        String[] weekDays = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" };
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0) {
            w = 0;
        }

        return weekDays[w];
    }

    public static String format(Date date, String pattern) {
        return format(date, pattern, "");
    }

    public static String format(Date date, String pattern, String defaultValue) {
        if (date == null || pattern == null) {
            return defaultValue;
        }

        try {
            return new SimpleDateFormat(pattern).format(date);
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * 字符串转java.util.Date,如转换失败返回null
     * **/
    public static Date parseDate(String time, String pattern) {
        return parseDate(time, pattern, null);
    }

    /**
     * 字符串转java.util.Date,如转换失败返回defaultDate
     * **/
    public static Date parseDate(String time, String pattern, Date defaultDate) {
        if (StringUtil.isNullOrEmpty(time) || StringUtil.isNullOrEmpty(pattern)) {
            return defaultDate;
        }
        try {
            return new SimpleDateFormat(pattern).parse(time);
        } catch (ParseException e) {
            return defaultDate;
        }
    }


    /**
     * 获取当前日期是一年中第几周
     */
    public static Integer getWeekOfYear(Date date) {
        Calendar c = new GregorianCalendar();
        c.setFirstDayOfWeek(Calendar.MONDAY);
        c.setMinimalDaysInFirstWeek(7);
        c.setTime(date);

        return c.get(Calendar.WEEK_OF_YEAR);
    }


    /**
     * 获取某一年的总周数
     *
     * @param year
     * @return
     */
    public static Integer getWeekCountOfYear(int year) {
        Calendar c = new GregorianCalendar();
        c.set(year, Calendar.DECEMBER, 31, 23, 59, 59);
        return getWeekOfYear(c.getTime());
    }

    /**
     * 获取指定日期所在周的第一天
     *
     * @param date
     * @return
     */
    public static Date getFirstDayOfWeek(Date date) {
        Calendar c = new GregorianCalendar();
        c.setFirstDayOfWeek(Calendar.MONDAY);
        c.setTime(date);
        // Monday
        c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek());
        return c.getTime();
    }

    /**
     * 获取指定日期所在周的最后一天
     *
     * @param date
     * @return
     */
    public static Date getLastDayOfWeek(Date date) {
        Calendar c = new GregorianCalendar();
        c.setFirstDayOfWeek(Calendar.MONDAY);
        c.setTime(date);
        // Sunday
        c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek() + 6);
        return c.getTime();
    }

    /**
     * 获取某年某周的第一天
     *
     * @param year 目标年份
     * @param week 目标周数
     * @return
     */
    public static Date getFirstDayOfWeek(int year, int week) {
        Calendar c = new GregorianCalendar();
        c.set(Calendar.YEAR, year);
        c.set(Calendar.MONTH, Calendar.JANUARY);
        c.set(Calendar.DATE, 1);

        Calendar cal = (GregorianCalendar) c.clone();
        cal.add(Calendar.DATE, week * 7);

        return getFirstDayOfWeek(cal.getTime());
    }

    /**
     * 获取某年某周的最后一天
     *
     * @param year 目标年份
     * @param week 目标周数
     * @return
     */
    public static Date getLastDayOfWeek(int year, int week) {
        Calendar c = new GregorianCalendar();
        c.set(Calendar.YEAR, year);
        c.set(Calendar.MONTH, Calendar.JANUARY);
        c.set(Calendar.DATE, 1);

        Calendar cal = (GregorianCalendar) c.clone();
        cal.add(Calendar.DATE, week * 7);

        return getLastDayOfWeek(cal.getTime());
    }

    /**
     * 获取某年某月的第一天
     *
     * @param year  目标年份
     * @param month 目标月份
     * @return
     */
    public static Date getFirstDayOfMonth(int year, int month) {
        month = month - 1;
        Calendar c = Calendar.getInstance();
        c.set(Calendar.YEAR, year);
        c.set(Calendar.MONTH, month);

        int day = c.getActualMinimum(Calendar.DAY_OF_MONTH);

        c.set(Calendar.DAY_OF_MONTH, day);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime();
    }

    /**
     * 获取某年某月的最后一天
     *
     * @param year  目标年份
     * @param month 目标月份
     * @return
     */
    public static Date getLastDayOfMonth(int year, int month) {
        month = month - 1;
        Calendar c = Calendar.getInstance();
        c.set(Calendar.YEAR, year);
        c.set(Calendar.MONTH, month);
        int day = c.getActualMaximum(Calendar.DAY_OF_MONTH);
        c.set(Calendar.DAY_OF_MONTH, day);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        return c.getTime();
    }



    /**
     * 获得两个日期的时间戳之差 毫秒差
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 返回两个日期之间的时间差
     */
    public static Long getDistanceTimestamp(Date startDate, Date endDate) {
        return endDate.getTime() - startDate.getTime();
    }

    /**
     * 判断二个时间是否为同年同月
     *
     * @param date1 日期
     * @param date2 日期
     * @return 如果是同年同月返回true
     */
    public static Boolean isSameMonth(Date date1, Date date2) {
        int year1 = getYear(date1);
        int year2 = getYear(date2);
        int month1 = getMonth(date1);
        int month2 = getMonth(date2);
        if (year1 == year2 && month1 == month2) {
            return true;
        }
        return false;
    }


    /**
     * 两个时间之间相差距离多少天
     */
    public static Long getDistanceDays(Date date1, Date date2) {
        long time1 = date1.getTime();
        long time2 = date2.getTime();
        long diff;
        if (time1 < time2) {
            diff = time2 - time1;
        } else {
            diff = time1 - time2;
        }
        long days = diff / (1000 * 60 * 60 * 24);
        return days;
    }

    /**
     * 获取指定时间的那天 00:00:00.000 的时间
     */
    public static Date getDayBeginTime(final Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime();
    }

    /**
     * 获取指定时间的那天 23:59:59.999 的时间
     */
    public static Date getDayEndTime(final Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        return c.getTime();
    }


    /**
     * 日期是否同一天
     * **/
    public static boolean isSameDay(Date date1, Date date2) {
        if (date1 == null || date2 == null) {
            return false;
        }

        return date1.getYear() == date2.getYear()
                && date1.getMonth() == date2.getMonth()
                && date1.getDate() == date2.getDate();
    }

    /**
     * 日期字符转换，例：convert("2020-06-22", "yyyy-MM-dd", "yyyy/MM/dd");
     * **/
    public static String convertDateStr(String dateStr, String inFormat, String outFormat) {
        return format(parseDate(dateStr, inFormat), outFormat);
    }

    /**
     * 日期加减, 调用Calendar.add方法
     */
    public static Date add(Date date, int field, int days) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(field, days);
        return calendar.getTime();
    }
    /**
     * 日期加减, 调用Calendar.add方法
     */
    public static String add(String date,String format, int field, int days) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(parseDate(date, format));
        calendar.add(field, days);
        return format(calendar.getTime(),format);
    }

    /**
     * 格式化Date时间，
     *
     * @param time         Date类型时间
     * @param pattern      String类型格式
     * @param defaultValue 默认值为当前时间Date
     * @return 格式化后的字符串
     */
    @Deprecated
    public static String format(ZonedDateTime time, String pattern, final ZonedDateTime defaultValue) {
        try {
            return format(time, pattern);
        } catch (Exception e) {
            if (defaultValue != null) {
                return format(defaultValue, pattern);
            } else {
                return format(ZonedDateTime.now(), pattern);
            }
        }
    }

    /**
     * 格式化Date时间
     *
     * @param time         Date类型时间
     * @param timeFormat   String类型格式
     * @param defaultValue 默认时间值String类型
     * @return 格式化后的字符串
     */
    @Deprecated
    public static String format(ZonedDateTime time, String timeFormat, final String defaultValue) {
        try {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern(timeFormat);
            return formatter.format(time);
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * 格式化日期对象 通过 formatPattern
     *
     * @param date          日期对象
     * @param formatPattern 格式化模板
     * @return 返回格式化结果
     */
    @Deprecated
    public static String format(ZonedDateTime date, String formatPattern) {
        DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern(formatPattern);
        return dateFormat.format(date);
    }

    /**
     * 格式化日期对象 通过 formatPattern
     *
     * @param date       日期对象
     * @param dateFormat 格式化日期的工具类
     * @return 返回格式化结果
     */
    @Deprecated
    public static String format(ZonedDateTime date, DateTimeFormatter dateFormat) {
        return dateFormat.format(date);
    }

    @Deprecated
    public static String format(String target, String fromPattern, String toPattern) {
        if (StringUtil.isNullOrEmptyAny(target, fromPattern, toPattern)) {
            return target;
        }
        ZonedDateTime dateTime = parse(target, fromPattern);
        return format(dateTime, toPattern);
    }

    /**
     * 格式化String时间
     *
     * @param time          String类型时间
     * @param formatPattern String类型格式
     * @return 格式化后的Date日期
     */
    @Deprecated
    public static ZonedDateTime parse(String time, String formatPattern) {
        if (StringUtil.isNullOrEmpty(time)) {
            return null;
        }

        DateTimeFormatter deteFormatter = DateTimeFormatter.ofPattern(formatPattern);
        // 需要确认时区 因为使用的 ZonedDateTime 时区信息从ZoneId中拿到
        return LocalDate.parse(time, deteFormatter).atStartOfDay(ZoneId.systemDefault());
    }

    /**
     * 格式化String时间
     *
     * @param strTime       String类型时间
     * @param formatPattern String类型格式
     * @param defaultValue  异常时返回的默认值
     * @return 返回格式化时间
     */
    @Deprecated
    public static ZonedDateTime parse(String strTime, String formatPattern, ZonedDateTime defaultValue) {
        try {
            return parse(strTime, formatPattern);
        } catch (Exception e) {
            return defaultValue;
        }
    }
    /**
     * 计算2个时间之间的分钟差
     * @param date1
     * @param date2
     * @return
     */
    public static long getDistanceMinutes(Date date1, Date date2) {
        long time1 = date1.getTime();
        long time2 = date2.getTime();
        long diff;
        if (time1 < time2) {
            diff = time2 - time1;
        } else {
            diff = time1 - time2;
        }
        return (diff / (60 * 1000));
    }

    /**
     * 判断给定年份是否为闰年
     * @param year
     * @return
     */
    public static boolean isLeafYear(int year) {
        if (year <= 0) return false;
        return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
    }
}
